// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/proxy/proxy_service.h"

#include <cstdarg>
#include <string>
#include <vector>

#include "base/format_macros.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/base/proxy_delegate.h"
#include "net/base/test_completion_callback.h"
#include "net/log/net_log.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_entry.h"
#include "net/log/test_net_log_util.h"
#include "net/proxy/dhcp_proxy_script_fetcher.h"
#include "net/proxy/mock_proxy_resolver.h"
#include "net/proxy/mock_proxy_script_fetcher.h"
#include "net/proxy/proxy_config_service.h"
#include "net/proxy/proxy_resolver.h"
#include "net/proxy/proxy_script_fetcher.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

using base::ASCIIToUTF16;

// TODO(eroman): Write a test which exercises
//              ProxyService::SuspendAllPendingRequests().
namespace net {
namespace {

    // This polling policy will decide to poll every 1 ms.
    class ImmediatePollPolicy : public ProxyService::PacPollPolicy {
    public:
        ImmediatePollPolicy() { }

        Mode GetNextDelay(int error,
            base::TimeDelta current_delay,
            base::TimeDelta* next_delay) const override
        {
            *next_delay = base::TimeDelta::FromMilliseconds(1);
            return MODE_USE_TIMER;
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(ImmediatePollPolicy);
    };

    // This polling policy chooses a fantastically large delay. In other words, it
    // will never trigger a poll
    class NeverPollPolicy : public ProxyService::PacPollPolicy {
    public:
        NeverPollPolicy() { }

        Mode GetNextDelay(int error,
            base::TimeDelta current_delay,
            base::TimeDelta* next_delay) const override
        {
            *next_delay = base::TimeDelta::FromDays(60);
            return MODE_USE_TIMER;
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(NeverPollPolicy);
    };

    // This polling policy starts a poll immediately after network activity.
    class ImmediateAfterActivityPollPolicy : public ProxyService::PacPollPolicy {
    public:
        ImmediateAfterActivityPollPolicy() { }

        Mode GetNextDelay(int error,
            base::TimeDelta current_delay,
            base::TimeDelta* next_delay) const override
        {
            *next_delay = base::TimeDelta();
            return MODE_START_AFTER_ACTIVITY;
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(ImmediateAfterActivityPollPolicy);
    };

    // This test fixture is used to partially disable the background polling done by
    // the ProxyService (which it uses to detect whenever its PAC script contents or
    // WPAD results have changed).
    //
    // We disable the feature by setting the poll interval to something really
    // large, so it will never actually be reached even on the slowest bots that run
    // these tests.
    //
    // We disable the polling in order to avoid any timing dependencies in the
    // tests. If the bot were to run the tests very slowly and we hadn't disabled
    // polling, then it might start a background re-try in the middle of our test
    // and confuse our expectations leading to flaky failures.
    //
    // The tests which verify the polling code re-enable the polling behavior but
    // are careful to avoid timing problems.
    class ProxyServiceTest : public testing::Test {
    protected:
        void SetUp() override
        {
            testing::Test::SetUp();
            previous_policy_ = ProxyService::set_pac_script_poll_policy(&never_poll_policy_);
        }

        void TearDown() override
        {
            // Restore the original policy.
            ProxyService::set_pac_script_poll_policy(previous_policy_);
            testing::Test::TearDown();
        }

    private:
        NeverPollPolicy never_poll_policy_;
        const ProxyService::PacPollPolicy* previous_policy_;
    };

    const char kValidPacScript1[] = "pac-script-v1-FindProxyForURL";
    const char kValidPacScript2[] = "pac-script-v2-FindProxyForURL";

    class MockProxyConfigService : public ProxyConfigService {
    public:
        explicit MockProxyConfigService(const ProxyConfig& config)
            : availability_(CONFIG_VALID)
            , config_(config)
        {
        }

        explicit MockProxyConfigService(const std::string& pac_url)
            : availability_(CONFIG_VALID)
            , config_(ProxyConfig::CreateFromCustomPacURL(GURL(pac_url)))
        {
        }

        void AddObserver(Observer* observer) override
        {
            observers_.AddObserver(observer);
        }

        void RemoveObserver(Observer* observer) override
        {
            observers_.RemoveObserver(observer);
        }

        ConfigAvailability GetLatestProxyConfig(ProxyConfig* results) override
        {
            if (availability_ == CONFIG_VALID)
                *results = config_;
            return availability_;
        }

        void SetConfig(const ProxyConfig& config)
        {
            availability_ = CONFIG_VALID;
            config_ = config;
            FOR_EACH_OBSERVER(Observer, observers_,
                OnProxyConfigChanged(config_, availability_));
        }

    private:
        ConfigAvailability availability_;
        ProxyConfig config_;
        base::ObserverList<Observer, true> observers_;
    };

    // A test network delegate that exercises the OnResolveProxy callback.
    class TestResolveProxyDelegate : public ProxyDelegate {
    public:
        TestResolveProxyDelegate()
            : on_resolve_proxy_called_(false)
            , add_proxy_(false)
            , remove_proxy_(false)
            , proxy_service_(nullptr)
        {
        }

        void OnResolveProxy(const GURL& url,
            const std::string& method,
            int load_flags,
            const ProxyService& proxy_service,
            ProxyInfo* result) override
        {
            method_ = method;
            on_resolve_proxy_called_ = true;
            proxy_service_ = &proxy_service;
            DCHECK(!add_proxy_ || !remove_proxy_);
            if (add_proxy_) {
                result->UseNamedProxy("delegate_proxy.com");
            } else if (remove_proxy_) {
                result->UseDirect();
            }
        }

        bool on_resolve_proxy_called() const
        {
            return on_resolve_proxy_called_;
        }

        const std::string& method() const { return method_; }

        void set_add_proxy(bool add_proxy)
        {
            add_proxy_ = add_proxy;
        }

        void set_remove_proxy(bool remove_proxy)
        {
            remove_proxy_ = remove_proxy;
        }

        const ProxyService* proxy_service() const
        {
            return proxy_service_;
        }

        void OnTunnelConnectCompleted(const HostPortPair& endpoint,
            const HostPortPair& proxy_server,
            int net_error) override { }
        void OnFallback(const ProxyServer& bad_proxy, int net_error) override { }
        void OnBeforeTunnelRequest(const HostPortPair& proxy_server,
            HttpRequestHeaders* extra_headers) override { }
        void OnTunnelHeadersReceived(
            const HostPortPair& origin,
            const HostPortPair& proxy_server,
            const HttpResponseHeaders& response_headers) override { }
        bool IsTrustedSpdyProxy(const net::ProxyServer& proxy_server) override
        {
            return true;
        }

    private:
        bool on_resolve_proxy_called_;
        bool add_proxy_;
        bool remove_proxy_;
        std::string method_;
        const ProxyService* proxy_service_;
    };

    // A test network delegate that exercises the OnProxyFallback callback.
    class TestProxyFallbackProxyDelegate : public ProxyDelegate {
    public:
        TestProxyFallbackProxyDelegate()
            : on_proxy_fallback_called_(false)
            , proxy_fallback_net_error_(OK)
        {
        }

        // ProxyDelegate implementation:
        void OnResolveProxy(const GURL& url,
            const std::string& method,
            int load_flags,
            const ProxyService& proxy_service,
            ProxyInfo* result) override { }
        void OnTunnelConnectCompleted(const HostPortPair& endpoint,
            const HostPortPair& proxy_server,
            int net_error) override { }
        void OnFallback(const ProxyServer& bad_proxy, int net_error) override
        {
            proxy_server_ = bad_proxy;
            proxy_fallback_net_error_ = net_error;
            on_proxy_fallback_called_ = true;
        }
        void OnBeforeTunnelRequest(const HostPortPair& proxy_server,
            HttpRequestHeaders* extra_headers) override { }
        void OnTunnelHeadersReceived(
            const HostPortPair& origin,
            const HostPortPair& proxy_server,
            const HttpResponseHeaders& response_headers) override { }
        bool IsTrustedSpdyProxy(const net::ProxyServer& proxy_server) override
        {
            return true;
        }

        bool on_proxy_fallback_called() const
        {
            return on_proxy_fallback_called_;
        }

        const ProxyServer& proxy_server() const
        {
            return proxy_server_;
        }

        int proxy_fallback_net_error() const
        {
            return proxy_fallback_net_error_;
        }

    private:
        bool on_proxy_fallback_called_;
        ProxyServer proxy_server_;
        int proxy_fallback_net_error_;
    };

    using RequestMap = std::map<GURL, scoped_refptr<MockAsyncProxyResolver::Request>>;

    // Given a list of requests |list| from a MockAsyncProxyResolver and a list of
    // target URLs |_urls|, asserts that the set of URLs of the requests appearing
    // in |list| is exactly the set of URLs in |_urls|, and produces a RequestMap in
    // |*map| containing the requests corresponding to those target |_urls|.
    //
    // Note that this function must return void to allow use of gtest's ASSERT_*
    // macros inside it.
    RequestMap GetRequestsForURLs(
        const MockAsyncProxyResolver::RequestsList& requests,
        const std::vector<GURL>& urls)
    {
        RequestMap map;

        for (const auto& it : requests)
            map[it->url()] = it;

        if (urls.size() != map.size()) {
            ADD_FAILURE() << "map size (" << map.size() << ") != urls size ("
                          << urls.size() << ")";
            return map;
        }
        for (const auto& it : urls) {
            if (map.count(it) != 1U) {
                ADD_FAILURE() << "url not in map: " << it.spec();
                break;
            }
        }
        return map;
    }

    // Given a MockAsyncProxyResolver |resolver| and some GURLs, validates that the
    // set of pending request URLs for |resolver| is exactly the supplied list of
    // URLs and returns a map from URLs to the corresponding pending requests.
    RequestMap GetPendingRequestsForURLs(const MockAsyncProxyResolver& resolver,
        const GURL& url1 = GURL(),
        const GURL& url2 = GURL(),
        const GURL& url3 = GURL())
    {
        std::vector<GURL> urls;
        if (!url1.is_empty())
            urls.push_back(url1);
        if (!url2.is_empty())
            urls.push_back(url2);
        if (!url3.is_empty())
            urls.push_back(url3);
        return GetRequestsForURLs(resolver.pending_requests(), urls);
    }

    // Given a MockAsyncProxyResolver |resolver| and some GURLs, validates that the
    // set of cancelled request URLs for |resolver| is exactly the supplied list of
    // URLs and returns a map from URLs to the corresponding cancelled requests.
    RequestMap GetCancelledRequestsForURLs(const MockAsyncProxyResolver& resolver,
        const GURL& url1 = GURL(),
        const GURL& url2 = GURL(),
        const GURL& url3 = GURL())
    {
        std::vector<GURL> urls;
        if (!url1.is_empty())
            urls.push_back(url1);
        if (!url2.is_empty())
            urls.push_back(url2);
        if (!url3.is_empty())
            urls.push_back(url3);
        return GetRequestsForURLs(resolver.cancelled_requests(), urls);
    }

} // namespace

TEST_F(ProxyServiceTest, Direct)
{
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);
    ProxyService service(
        base::WrapUnique(new MockProxyConfigService(ProxyConfig::CreateDirect())),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    TestCompletionCallback callback;
    BoundTestNetLog log;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr, log.bound());
    EXPECT_EQ(OK, rv);
    EXPECT_TRUE(factory->pending_requests().empty());

    EXPECT_TRUE(info.is_direct());
    EXPECT_TRUE(info.proxy_resolve_start_time().is_null());
    EXPECT_TRUE(info.proxy_resolve_end_time().is_null());

    // Check the NetLog was filled correctly.
    TestNetLogEntry::List entries;
    log.GetEntries(&entries);

    EXPECT_EQ(3u, entries.size());
    EXPECT_TRUE(LogContainsBeginEvent(
        entries, 0, NetLog::TYPE_PROXY_SERVICE));
    EXPECT_TRUE(LogContainsEvent(
        entries, 1, NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
        NetLog::PHASE_NONE));
    EXPECT_TRUE(LogContainsEndEvent(
        entries, 2, NetLog::TYPE_PROXY_SERVICE));
}

TEST_F(ProxyServiceTest, OnResolveProxyCallbackAddProxy)
{
    ProxyConfig config;
    config.proxy_rules().ParseFromString("foopy1:8080");
    config.set_auto_detect(false);
    config.proxy_rules().bypass_rules.ParseFromString("*.org");

    ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
        nullptr, nullptr);

    GURL url("http://www.google.com/");
    GURL bypass_url("http://internet.org");

    ProxyInfo info;
    TestCompletionCallback callback;
    BoundTestNetLog log;

    // First, warm up the ProxyService.
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr, log.bound());
    EXPECT_EQ(OK, rv);

    // Verify that network delegate is invoked.
    TestResolveProxyDelegate delegate;
    rv = service.ResolveProxy(url, "GET", LOAD_NORMAL, &info, callback.callback(),
        nullptr, &delegate, log.bound());
    EXPECT_TRUE(delegate.on_resolve_proxy_called());
    EXPECT_EQ(&service, delegate.proxy_service());
    EXPECT_EQ(delegate.method(), "GET");

    // Verify that the ProxyDelegate's behavior is stateless across
    // invocations of ResolveProxy. Start by having the callback add a proxy
    // and checking that subsequent requests are not affected.
    delegate.set_add_proxy(true);

    // Callback should interpose:
    rv = service.ResolveProxy(url, "GET", LOAD_NORMAL, &info, callback.callback(),
        nullptr, &delegate, log.bound());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ(info.proxy_server().host_port_pair().host(), "delegate_proxy.com");
    delegate.set_add_proxy(false);

    // Check non-bypassed URL:
    rv = service.ResolveProxy(url, "GET", LOAD_NORMAL, &info, callback.callback(),
        nullptr, &delegate, log.bound());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ(info.proxy_server().host_port_pair().host(), "foopy1");

    // Check bypassed URL:
    rv = service.ResolveProxy(bypass_url, "GET", LOAD_NORMAL, &info,
        callback.callback(), nullptr, &delegate,
        log.bound());
    EXPECT_TRUE(info.is_direct());
}

TEST_F(ProxyServiceTest, OnResolveProxyCallbackRemoveProxy)
{
    // Same as OnResolveProxyCallbackAddProxy, but verify that the
    // ProxyDelegate's behavior is stateless across invocations after it
    // *removes* a proxy.
    ProxyConfig config;
    config.proxy_rules().ParseFromString("foopy1:8080");
    config.set_auto_detect(false);
    config.proxy_rules().bypass_rules.ParseFromString("*.org");

    ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
        nullptr, nullptr);

    GURL url("http://www.google.com/");
    GURL bypass_url("http://internet.org");

    ProxyInfo info;
    TestCompletionCallback callback;
    BoundTestNetLog log;

    // First, warm up the ProxyService.
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr, log.bound());
    EXPECT_EQ(OK, rv);

    TestResolveProxyDelegate delegate;
    delegate.set_remove_proxy(true);

    // Callback should interpose:
    rv = service.ResolveProxy(url, "GET", LOAD_NORMAL, &info, callback.callback(),
        nullptr, &delegate, log.bound());
    EXPECT_TRUE(info.is_direct());
    delegate.set_remove_proxy(false);

    // Check non-bypassed URL:
    rv = service.ResolveProxy(url, "GET", LOAD_NORMAL, &info, callback.callback(),
        nullptr, &delegate, log.bound());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ(info.proxy_server().host_port_pair().host(), "foopy1");

    // Check bypassed URL:
    rv = service.ResolveProxy(bypass_url, "GET", LOAD_NORMAL, &info,
        callback.callback(), nullptr, &delegate,
        log.bound());
    EXPECT_TRUE(info.is_direct());
}

TEST_F(ProxyServiceTest, PAC)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    TestCompletionCallback callback;
    ProxyService::PacRequest* request;
    BoundTestNetLog log;

    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), &request, nullptr, log.bound());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, service.GetLoadState(request));

    ASSERT_EQ(1u, factory->pending_requests().size());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy:80", info.proxy_server().ToURI());
    EXPECT_TRUE(info.did_use_pac_script());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // Check the NetLog was filled correctly.
    TestNetLogEntry::List entries;
    log.GetEntries(&entries);

    EXPECT_EQ(5u, entries.size());
    EXPECT_TRUE(LogContainsBeginEvent(
        entries, 0, NetLog::TYPE_PROXY_SERVICE));
    EXPECT_TRUE(LogContainsBeginEvent(
        entries, 1, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
    EXPECT_TRUE(LogContainsEndEvent(
        entries, 2, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
    EXPECT_TRUE(LogContainsEndEvent(
        entries, 4, NetLog::TYPE_PROXY_SERVICE));
}

// Test that the proxy resolver does not see the URL's username/password
// or its reference section.
TEST_F(ProxyServiceTest, PAC_NoIdentityOrHash)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://username:password@www.google.com/?ref#hash#hash");

    ProxyInfo info;
    TestCompletionCallback callback;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    // The URL should have been simplified, stripping the username/password/hash.
    EXPECT_EQ(GURL("http://www.google.com/?ref"),
        resolver.pending_requests()[0]->url());

    // We end here without ever completing the request -- destruction of
    // ProxyService will cancel the outstanding request.
}

TEST_F(ProxyServiceTest, PAC_FailoverWithoutDirect)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy:8080");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy:8080", info.proxy_server().ToURI());
    EXPECT_TRUE(info.did_use_pac_script());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // Now, imagine that connecting to foopy:8080 fails: there is nothing
    // left to fallback to, since our proxy list was NOT terminated by
    // DIRECT.
    TestResolveProxyDelegate proxy_delegate;
    TestCompletionCallback callback2;
    ProxyServer expected_proxy_server = info.proxy_server();
    rv = service.ReconsiderProxyAfterError(
        url, "GET", LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, &proxy_delegate, BoundNetLog());
    // ReconsiderProxyAfterError returns error indicating nothing left.
    EXPECT_EQ(ERR_FAILED, rv);
    EXPECT_TRUE(info.is_empty());
}

// Test that if the execution of the PAC script fails (i.e. javascript runtime
// error), and the PAC settings are non-mandatory, that we fall-back to direct.
TEST_F(ProxyServiceTest, PAC_RuntimeError)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://this-causes-js-error/");

    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Simulate a failure in the PAC executor.
    resolver.pending_requests()[0]->CompleteNow(ERR_PAC_SCRIPT_FAILED);

    EXPECT_EQ(OK, callback1.WaitForResult());

    // Since the PAC script was non-mandatory, we should have fallen-back to
    // DIRECT.
    EXPECT_TRUE(info.is_direct());
    EXPECT_TRUE(info.did_use_pac_script());
    EXPECT_EQ(1, info.config_id());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}

// The proxy list could potentially contain the DIRECT fallback choice
// in a location other than the very end of the list, and could even
// specify it multiple times.
//
// This is not a typical usage, but we will obey it.
// (If we wanted to disallow this type of input, the right place to
// enforce it would be in parsing the PAC result string).
//
// This test will use the PAC result string:
//
//   "DIRECT ; PROXY foobar:10 ; DIRECT ; PROXY foobar:20"
//
// For which we expect it to try DIRECT, then foobar:10, then DIRECT again,
// then foobar:20, and then give up and error.
//
// The important check of this test is to make sure that DIRECT is not somehow
// cached as being a bad proxy.
TEST_F(ProxyServiceTest, PAC_FailoverAfterDirect)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UsePacString(
        "DIRECT ; PROXY foobar:10 ; DIRECT ; PROXY foobar:20");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_TRUE(info.is_direct());

    // Fallback 1.
    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foobar:10", info.proxy_server().ToURI());

    // Fallback 2.
    TestResolveProxyDelegate proxy_delegate;
    ProxyServer expected_proxy_server3 = info.proxy_server();
    TestCompletionCallback callback3;
    rv = service.ReconsiderProxyAfterError(
        url, "GET", LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback3.callback(), nullptr, &proxy_delegate, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_TRUE(info.is_direct());

    // Fallback 3.
    ProxyServer expected_proxy_server4 = info.proxy_server();
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, "GET", LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback4.callback(), nullptr, &proxy_delegate, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foobar:20", info.proxy_server().ToURI());

    // Fallback 4 -- Nothing to fall back to!
    ProxyServer expected_proxy_server5 = info.proxy_server();
    TestCompletionCallback callback5;
    rv = service.ReconsiderProxyAfterError(
        url, "GET", LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback5.callback(), nullptr, &proxy_delegate, BoundNetLog());
    EXPECT_EQ(ERR_FAILED, rv);
    EXPECT_TRUE(info.is_empty());
}

TEST_F(ProxyServiceTest, PAC_ConfigSourcePropagates)
{
    // Test whether the ProxyConfigSource set by the ProxyConfigService is applied
    // to ProxyInfo after the proxy is resolved via a PAC script.
    ProxyConfig config = ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac"));
    config.set_source(PROXY_CONFIG_SOURCE_TEST);

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Resolve something.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr,
        BoundNetLog());
    ASSERT_EQ(ERR_IO_PENDING, rv);
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
    ASSERT_EQ(1u, resolver.pending_requests().size());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback.WaitForResult());
    EXPECT_EQ(PROXY_CONFIG_SOURCE_TEST, info.config_source());
    EXPECT_TRUE(info.did_use_pac_script());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}

TEST_F(ProxyServiceTest, ProxyResolverFails)
{
    // Test what happens when the ProxyResolver fails. The download and setting
    // of the PAC script have already succeeded, so this corresponds with a
    // javascript runtime error while calling FindProxyForURL().

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start first resolve request.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Fail the first resolve request in MockAsyncProxyResolver.
    resolver.pending_requests()[0]->CompleteNow(ERR_FAILED);

    // Although the proxy resolver failed the request, ProxyService implicitly
    // falls-back to DIRECT.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_TRUE(info.is_direct());

    // Failed PAC executions still have proxy resolution times.
    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // The second resolve request will try to run through the proxy resolver,
    // regardless of whether the first request failed in it.
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This time we will have the resolver succeed (perhaps the PAC script has
    // a dependency on the current time).
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy_valid:8080");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}

TEST_F(ProxyServiceTest, ProxyResolverTerminatedDuringRequest)
{
    // Test what happens when the ProxyResolver fails with a fatal error while
    // a GetProxyForURL() call is in progress.

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start first resolve request.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), net::LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, factory->pending_requests().size());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Fail the first resolve request in MockAsyncProxyResolver.
    resolver.pending_requests()[0]->CompleteNow(ERR_PAC_SCRIPT_TERMINATED);

    // Although the proxy resolver failed the request, ProxyService implicitly
    // falls-back to DIRECT.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_TRUE(info.is_direct());

    // Failed PAC executions still have proxy resolution times.
    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // With no other requests, the ProxyService waits for a new request before
    // initializing a new ProxyResolver.
    EXPECT_TRUE(factory->pending_requests().empty());

    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url, std::string(), net::LOAD_NORMAL, &info,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, factory->pending_requests().size());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This time we will have the resolver succeed.
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy_valid:8080");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}

TEST_F(ProxyServiceTest,
    ProxyResolverTerminatedDuringRequestWithConcurrentRequest)
{
    // Test what happens when the ProxyResolver fails with a fatal error while
    // a GetProxyForURL() call is in progress.

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start two resolve requests.
    GURL url1("http://www.google.com/");
    GURL url2("https://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url1, std::string(), net::LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url2, std::string(), net::LOAD_NORMAL, &info,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, factory->pending_requests().size());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    RequestMap requests = GetPendingRequestsForURLs(resolver, url1, url2);

    // Fail the first resolve request in MockAsyncProxyResolver.
    requests[url1]->CompleteNow(ERR_PAC_SCRIPT_TERMINATED);

    // Although the proxy resolver failed the request, ProxyService implicitly
    // falls-back to DIRECT.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_TRUE(info.is_direct());

    // Failed PAC executions still have proxy resolution times.
    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // The second request is cancelled when the proxy resolver terminates.
    requests = GetCancelledRequestsForURLs(resolver, url2);

    // Since a second request was in progress, the ProxyService starts
    // initializating a new ProxyResolver.
    ASSERT_EQ(1u, factory->pending_requests().size());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    requests = GetPendingRequestsForURLs(resolver, url2);

    // This request succeeds.
    requests[url2]->results()->UseNamedProxy("foopy_valid:8080");
    requests[url2]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}

TEST_F(ProxyServiceTest, ProxyScriptFetcherFailsDownloadingMandatoryPac)
{
    // Test what happens when the ProxyScriptResolver fails to download a
    // mandatory PAC script.

    ProxyConfig config(
        ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
    config.set_pac_mandatory(true);

    MockProxyConfigService* config_service = new MockProxyConfigService(config);

    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start first resolve request.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNow(ERR_FAILED, nullptr);

    ASSERT_EQ(0u, factory->pending_requests().size());
    // As the proxy resolver factory failed the request and is configured for a
    // mandatory PAC script, ProxyService must not implicitly fall-back to DIRECT.
    EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
        callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());

    // As the proxy resolver factory failed the request and is configured for a
    // mandatory PAC script, ProxyService must not implicitly fall-back to DIRECT.
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED, rv);
    EXPECT_FALSE(info.is_direct());
}

TEST_F(ProxyServiceTest, ProxyResolverFailsParsingJavaScriptMandatoryPac)
{
    // Test what happens when the ProxyResolver fails that is configured to use a
    // mandatory PAC script. The download of the PAC script has already
    // succeeded but the PAC script contains no valid javascript.

    ProxyConfig config(
        ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
    config.set_pac_mandatory(true);

    MockProxyConfigService* config_service = new MockProxyConfigService(config);

    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start resolve request.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // Downloading the PAC script succeeds.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");

    EXPECT_FALSE(fetcher->has_pending_request());
    ASSERT_EQ(0u, factory->pending_requests().size());

    // Since ProxyScriptDecider failed to identify a valid PAC and PAC was
    // mandatory for this configuration, the ProxyService must not implicitly
    // fall-back to DIRECT.
    EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
        callback.WaitForResult());
    EXPECT_FALSE(info.is_direct());
}

TEST_F(ProxyServiceTest, ProxyResolverFailsInJavaScriptMandatoryPac)
{
    // Test what happens when the ProxyResolver fails that is configured to use a
    // mandatory PAC script. The download and setting of the PAC script have
    // already succeeded, so this corresponds with a javascript runtime error
    // while calling FindProxyForURL().

    ProxyConfig config(
        ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));
    config.set_pac_mandatory(true);

    MockProxyConfigService* config_service = new MockProxyConfigService(config);

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start first resolve request.
    GURL url("http://www.google.com/");
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Fail the first resolve request in MockAsyncProxyResolver.
    resolver.pending_requests()[0]->CompleteNow(ERR_FAILED);

    // As the proxy resolver failed the request and is configured for a mandatory
    // PAC script, ProxyService must not implicitly fall-back to DIRECT.
    EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
        callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());

    // The second resolve request will try to run through the proxy resolver,
    // regardless of whether the first request failed in it.
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This time we will have the resolver succeed (perhaps the PAC script has
    // a dependency on the current time).
    resolver.pending_requests()[0]->results()->UseNamedProxy("foopy_valid:8080");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy_valid:8080", info.proxy_server().ToURI());
}

TEST_F(ProxyServiceTest, ProxyFallback)
{
    // Test what happens when we specify multiple proxy servers and some of them
    // are bad.

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    // Get the proxy information.
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first item is valid.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
    base::TimeTicks proxy_resolve_start_time = info.proxy_resolve_start_time();
    base::TimeTicks proxy_resolve_end_time = info.proxy_resolve_end_time();

    // Fake an error on the proxy.
    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    // Proxy times should not have been modified by fallback.
    EXPECT_EQ(proxy_resolve_start_time, info.proxy_resolve_start_time());
    EXPECT_EQ(proxy_resolve_end_time, info.proxy_resolve_end_time());

    // The second proxy should be specified.
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());
    // Report back that the second proxy worked.  This will globally mark the
    // first proxy as bad.
    TestProxyFallbackProxyDelegate test_delegate;
    service.ReportSuccess(info, &test_delegate);
    EXPECT_EQ("foopy1:8080", test_delegate.proxy_server().ToURI());
    EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED,
        test_delegate.proxy_fallback_net_error());

    TestCompletionCallback callback3;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver -- the second result is already known
    // to be bad, so we will not try to use it initially.
    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy3:7070;foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback3.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI());

    // Proxy times should have been updated, so get them again.
    EXPECT_LE(proxy_resolve_end_time, info.proxy_resolve_start_time());
    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
    proxy_resolve_start_time = info.proxy_resolve_start_time();
    proxy_resolve_end_time = info.proxy_resolve_end_time();

    // We fake another error. It should now try the third one.
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback4.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());

    // We fake another error. At this point we have tried all of the
    // proxy servers we thought were valid; next we try the proxy server
    // that was in our bad proxies map (foopy1:8080).
    TestCompletionCallback callback5;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback5.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // Fake another error, the last proxy is gone, the list should now be empty,
    // so there is nothing left to try.
    TestCompletionCallback callback6;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback6.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_FAILED, rv);
    EXPECT_FALSE(info.is_direct());
    EXPECT_TRUE(info.is_empty());

    // Proxy times should not have been modified by fallback.
    EXPECT_EQ(proxy_resolve_start_time, info.proxy_resolve_start_time());
    EXPECT_EQ(proxy_resolve_end_time, info.proxy_resolve_end_time());

    // Look up proxies again
    TestCompletionCallback callback7;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback7.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This time, the first 3 results have been found to be bad, but only the
    // first proxy has been confirmed ...
    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy3:7070;foopy2:9090;foopy4:9091");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // ... therefore, we should see the second proxy first.
    EXPECT_EQ(OK, callback7.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy3:7070", info.proxy_server().ToURI());

    EXPECT_LE(proxy_resolve_end_time, info.proxy_resolve_start_time());
    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    // TODO(nsylvain): Test that the proxy can be retried after the delay.
}

// This test is similar to ProxyFallback, but this time we have an explicit
// fallback choice to DIRECT.
TEST_F(ProxyServiceTest, ProxyFallbackToDirect)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    // Get the proxy information.
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UsePacString(
        "PROXY foopy1:8080; PROXY foopy2:9090; DIRECT");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Get the first result.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // Fake an error on the proxy.
    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    // Now we get back the second proxy.
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());

    // Fake an error on this proxy as well.
    TestCompletionCallback callback3;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback3.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    // Finally, we get back DIRECT.
    EXPECT_TRUE(info.is_direct());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());

    // Now we tell the proxy service that even DIRECT failed.
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback4.callback(), nullptr, nullptr, BoundNetLog());
    // There was nothing left to try after DIRECT, so we are out of
    // choices.
    EXPECT_EQ(ERR_FAILED, rv);
}

TEST_F(ProxyServiceTest, ProxyFallback_NewSettings)
{
    // Test proxy failover when new settings are available.

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    // Get the proxy information.
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // Set the result in proxy resolver.
    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first item is valid.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // Fake an error on the proxy, and also a new configuration on the proxy.
    config_service->SetConfig(
        ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy-new/proxy.pac")));

    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy-new/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first proxy is still there since the configuration changed.
    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // We fake another error. It should now ignore the first one.
    TestCompletionCallback callback3;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback3.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());

    // We simulate a new configuration.
    config_service->SetConfig(
        ProxyConfig::CreateFromCustomPacURL(
            GURL("http://foopy-new2/proxy.pac")));

    // We fake another error. It should go back to the first proxy.
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback4.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy-new2/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback4.WaitForResult());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}

TEST_F(ProxyServiceTest, ProxyFallback_BadConfig)
{
    // Test proxy failover when the configuration is bad.

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    // Get the proxy information.
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first item is valid.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // Fake a proxy error.
    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    // The first proxy is ignored, and the second one is selected.
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());

    // Fake a PAC failure.
    ProxyInfo info2;
    TestCompletionCallback callback3;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info2,
        callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This simulates a javascript runtime error in the PAC script.
    resolver.pending_requests()[0]->CompleteNow(ERR_FAILED);

    // Although the resolver failed, the ProxyService will implicitly fall-back
    // to a DIRECT connection.
    EXPECT_EQ(OK, callback3.WaitForResult());
    EXPECT_TRUE(info2.is_direct());
    EXPECT_FALSE(info2.is_empty());

    // The PAC script will work properly next time and successfully return a
    // proxy list. Since we have not marked the configuration as bad, it should
    // "just work" the next time we call it.
    ProxyInfo info3;
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info3,
        callback4.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first proxy is not there since the it was added to the bad proxies
    // list by the earlier ReconsiderProxyAfterError().
    EXPECT_EQ(OK, callback4.WaitForResult());
    EXPECT_FALSE(info3.is_direct());
    EXPECT_EQ("foopy1:8080", info3.proxy_server().ToURI());

    EXPECT_FALSE(info.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info.proxy_resolve_end_time().is_null());
    EXPECT_LE(info.proxy_resolve_start_time(), info.proxy_resolve_end_time());
}

TEST_F(ProxyServiceTest, ProxyFallback_BadConfigMandatory)
{
    // Test proxy failover when the configuration is bad.

    ProxyConfig config(
        ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac")));

    config.set_pac_mandatory(true);
    MockProxyConfigService* config_service = new MockProxyConfigService(config);

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    // Get the proxy information.
    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first item is valid.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    // Fake a proxy error.
    TestCompletionCallback callback2;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info,
        callback2.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    // The first proxy is ignored, and the second one is selected.
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI());

    // Fake a PAC failure.
    ProxyInfo info2;
    TestCompletionCallback callback3;
    rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info2,
        callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    // This simulates a javascript runtime error in the PAC script.
    resolver.pending_requests()[0]->CompleteNow(ERR_FAILED);

    // Although the resolver failed, the ProxyService will NOT fall-back
    // to a DIRECT connection as it is configured as mandatory.
    EXPECT_EQ(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED,
        callback3.WaitForResult());
    EXPECT_FALSE(info2.is_direct());
    EXPECT_TRUE(info2.is_empty());

    // The PAC script will work properly next time and successfully return a
    // proxy list. Since we have not marked the configuration as bad, it should
    // "just work" the next time we call it.
    ProxyInfo info3;
    TestCompletionCallback callback4;
    rv = service.ReconsiderProxyAfterError(
        url, std::string(), LOAD_NORMAL, ERR_PROXY_CONNECTION_FAILED, &info3,
        callback4.callback(), nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(url, resolver.pending_requests()[0]->url());

    resolver.pending_requests()[0]->results()->UseNamedProxy(
        "foopy1:8080;foopy2:9090");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // The first proxy is not there since the it was added to the bad proxies
    // list by the earlier ReconsiderProxyAfterError().
    EXPECT_EQ(OK, callback4.WaitForResult());
    EXPECT_FALSE(info3.is_direct());
    EXPECT_EQ("foopy1:8080", info3.proxy_server().ToURI());
}

TEST_F(ProxyServiceTest, ProxyBypassList)
{
    // Test that the proxy bypass rules are consulted.

    TestCompletionCallback callback[2];
    ProxyInfo info[2];
    ProxyConfig config;
    config.proxy_rules().ParseFromString("foopy1:8080;foopy2:9090");
    config.set_auto_detect(false);
    config.proxy_rules().bypass_rules.ParseFromString("*.org");

    ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
        nullptr, nullptr);

    int rv;
    GURL url1("http://www.webkit.org");
    GURL url2("http://www.webkit.com");

    // Request for a .org domain should bypass proxy.
    rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info[0],
        callback[0].callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_TRUE(info[0].is_direct());

    // Request for a .com domain hits the proxy.
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info[1],
        callback[1].callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy1:8080", info[1].proxy_server().ToURI());
}

TEST_F(ProxyServiceTest, MarkProxiesAsBadTests)
{
    ProxyConfig config;
    config.proxy_rules().ParseFromString(
        "http=foopy1:8080;http=foopy2:8080;http=foopy3.8080;http=foopy4:8080");
    config.set_auto_detect(false);

    ProxyList proxy_list;
    std::vector<ProxyServer> additional_bad_proxies;
    for (const ProxyServer& proxy_server :
        config.proxy_rules().proxies_for_http.GetAll()) {
        proxy_list.AddProxyServer(proxy_server);
        if (proxy_server == config.proxy_rules().proxies_for_http.Get())
            continue;

        additional_bad_proxies.push_back(proxy_server);
    }

    EXPECT_EQ(3u, additional_bad_proxies.size());

    ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
        nullptr, nullptr);
    ProxyInfo proxy_info;
    proxy_info.UseProxyList(proxy_list);
    const ProxyRetryInfoMap& retry_info = service.proxy_retry_info();
    service.MarkProxiesAsBadUntil(proxy_info, base::TimeDelta::FromSeconds(1),
        additional_bad_proxies, BoundNetLog());
    ASSERT_EQ(4u, retry_info.size());
    for (const ProxyServer& proxy_server :
        config.proxy_rules().proxies_for_http.GetAll()) {
        ProxyRetryInfoMap::const_iterator i = retry_info.find(proxy_server.host_port_pair().ToString());
        ASSERT_TRUE(i != retry_info.end());
    }
}

TEST_F(ProxyServiceTest, PerProtocolProxyTests)
{
    ProxyConfig config;
    config.proxy_rules().ParseFromString("http=foopy1:8080;https=foopy2:8080");
    config.set_auto_detect(false);
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("http://www.msn.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
    }
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("ftp://ftp.google.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_TRUE(info.is_direct());
        EXPECT_EQ("direct://", info.proxy_server().ToURI());
    }
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("https://webbranch.techcu.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
    }
    {
        config.proxy_rules().ParseFromString("foopy1:8080");
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("http://www.microsoft.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
    }
}

TEST_F(ProxyServiceTest, ProxyConfigSourcePropagates)
{
    // Test that the proxy config source is set correctly when resolving proxies
    // using manual proxy rules. Namely, the config source should only be set if
    // any of the rules were applied.
    {
        ProxyConfig config;
        config.set_source(PROXY_CONFIG_SOURCE_TEST);
        config.proxy_rules().ParseFromString("https=foopy2:8080");
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("http://www.google.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        ASSERT_EQ(OK, rv);
        // Should be SOURCE_TEST, even if there are no HTTP proxies configured.
        EXPECT_EQ(PROXY_CONFIG_SOURCE_TEST, info.config_source());
    }
    {
        ProxyConfig config;
        config.set_source(PROXY_CONFIG_SOURCE_TEST);
        config.proxy_rules().ParseFromString("https=foopy2:8080");
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("https://www.google.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        ASSERT_EQ(OK, rv);
        // Used the HTTPS proxy. So source should be TEST.
        EXPECT_EQ(PROXY_CONFIG_SOURCE_TEST, info.config_source());
    }
    {
        ProxyConfig config;
        config.set_source(PROXY_CONFIG_SOURCE_TEST);
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("http://www.google.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        ASSERT_EQ(OK, rv);
        // ProxyConfig is empty. Source should still be TEST.
        EXPECT_EQ(PROXY_CONFIG_SOURCE_TEST, info.config_source());
    }
}

// If only HTTP and a SOCKS proxy are specified, check if ftp/https queries
// fall back to the SOCKS proxy.
TEST_F(ProxyServiceTest, DefaultProxyFallbackToSOCKS)
{
    ProxyConfig config;
    config.proxy_rules().ParseFromString("http=foopy1:8080;socks=foopy2:1080");
    config.set_auto_detect(false);
    EXPECT_EQ(ProxyConfig::ProxyRules::TYPE_PROXY_PER_SCHEME,
        config.proxy_rules().type);

    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("http://www.msn.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());
    }
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("ftp://ftp.google.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
    }
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("https://webbranch.techcu.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
    }
    {
        ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
            nullptr, nullptr);
        GURL test_url("unknown://www.microsoft.com");
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service.ResolveProxy(test_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(OK, rv);
        EXPECT_FALSE(info.is_direct());
        EXPECT_EQ("socks4://foopy2:1080", info.proxy_server().ToURI());
    }
}

// Test cancellation of an in-progress request.
TEST_F(ProxyServiceTest, CancelInProgressRequest)
{
    const GURL url1("http://request1");
    const GURL url2("http://request2");
    const GURL url3("http://request3");
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start 3 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info1,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Successfully initialize the PAC script.
    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    GetPendingRequestsForURLs(resolver, url1);

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info2,
        callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    GetPendingRequestsForURLs(resolver, url1, url2);

    ProxyInfo info3;
    TestCompletionCallback callback3;
    rv = service.ResolveProxy(url3, std::string(), LOAD_NORMAL, &info3,
        callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);
    GetPendingRequestsForURLs(resolver, url1, url2, url3);

    // Cancel the second request
    service.CancelPacRequest(request2);

    RequestMap requests = GetPendingRequestsForURLs(resolver, url1, url3);

    // Complete the two un-cancelled requests.
    // We complete the last one first, just to mix it up a bit.
    requests[url3]->results()->UseNamedProxy("request3:80");
    requests[url3]->CompleteNow(OK);

    requests[url1]->results()->UseNamedProxy("request1:80");
    requests[url1]->CompleteNow(OK);

    // Complete and verify that requests ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    EXPECT_FALSE(callback2.have_result()); // Cancelled.
    GetCancelledRequestsForURLs(resolver, url2);

    EXPECT_EQ(OK, callback3.WaitForResult());
    EXPECT_EQ("request3:80", info3.proxy_server().ToURI());
}

// Test the initial PAC download for resolver that expects bytes.
TEST_F(ProxyServiceTest, InitialPACScriptDownload)
{
    const GURL url1("http://request1");
    const GURL url2("http://request2");
    const GURL url3("http://request3");
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 3 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    ProxyService::PacRequest* request1;
    int rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info1,
        callback1.callback(), &request1, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info2,
        callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ProxyInfo info3;
    TestCompletionCallback callback3;
    ProxyService::PacRequest* request3;
    rv = service.ResolveProxy(url3, std::string(), LOAD_NORMAL, &info3,
        callback3.callback(), &request3, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    EXPECT_EQ(LOAD_STATE_DOWNLOADING_PROXY_SCRIPT,
        service.GetLoadState(request1));
    EXPECT_EQ(LOAD_STATE_DOWNLOADING_PROXY_SCRIPT,
        service.GetLoadState(request2));
    EXPECT_EQ(LOAD_STATE_DOWNLOADING_PROXY_SCRIPT,
        service.GetLoadState(request3));

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, it will have been sent to the proxy
    // resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    RequestMap requests = GetPendingRequestsForURLs(resolver, url1, url2, url3);

    EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, service.GetLoadState(request1));
    EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, service.GetLoadState(request2));
    EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL, service.GetLoadState(request3));

    // Complete all the requests (in some order).

    requests[url3]->results()->UseNamedProxy("request3:80");
    requests[url3]->CompleteNow(OK);

    requests[url1]->results()->UseNamedProxy("request1:80");
    requests[url1]->CompleteNow(OK);

    requests[url2]->results()->UseNamedProxy("request2:80");
    requests[url2]->CompleteNow(OK);

    // Complete and verify that requests ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
    EXPECT_FALSE(info1.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info1.proxy_resolve_end_time().is_null());
    EXPECT_LE(info1.proxy_resolve_start_time(), info1.proxy_resolve_end_time());

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
    EXPECT_FALSE(info2.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info2.proxy_resolve_end_time().is_null());
    EXPECT_LE(info2.proxy_resolve_start_time(), info2.proxy_resolve_end_time());

    EXPECT_EQ(OK, callback3.WaitForResult());
    EXPECT_EQ("request3:80", info3.proxy_server().ToURI());
    EXPECT_FALSE(info3.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info3.proxy_resolve_end_time().is_null());
    EXPECT_LE(info3.proxy_resolve_start_time(), info3.proxy_resolve_end_time());
}

// Test changing the ProxyScriptFetcher while PAC download is in progress.
TEST_F(ProxyServiceTest, ChangeScriptFetcherWhilePACDownloadInProgress)
{
    const GURL url1("http://request1");
    const GURL url2("http://request2");
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 2 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info1,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info2,
        callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.

    // We now change out the ProxyService's script fetcher. We should restart
    // the initialization with the new fetcher.

    fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, it will have been sent to the proxy
    // resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    GetPendingRequestsForURLs(resolver, url1, url2);
}

// Test cancellation of a request, while the PAC script is being fetched.
TEST_F(ProxyServiceTest, CancelWhilePACFetching)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 3 requests.
    ProxyInfo info1;
    TestCompletionCallback callback1;
    ProxyService::PacRequest* request1;
    BoundTestNetLog log1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        &request1, nullptr, log1.bound());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ProxyInfo info3;
    TestCompletionCallback callback3;
    rv = service.ResolveProxy(GURL("http://request3"), std::string(), LOAD_NORMAL,
        &info3, callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // Cancel the first 2 requests.
    service.CancelPacRequest(request1);
    service.CancelPacRequest(request2);

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, it will have been sent to the
    // proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request3"), resolver.pending_requests()[0]->url());

    // Complete all the requests.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request3:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback3.WaitForResult());
    EXPECT_EQ("request3:80", info3.proxy_server().ToURI());

    EXPECT_TRUE(resolver.cancelled_requests().empty());

    EXPECT_FALSE(callback1.have_result()); // Cancelled.
    EXPECT_FALSE(callback2.have_result()); // Cancelled.

    TestNetLogEntry::List entries1;
    log1.GetEntries(&entries1);

    // Check the NetLog for request 1 (which was cancelled) got filled properly.
    EXPECT_EQ(4u, entries1.size());
    EXPECT_TRUE(LogContainsBeginEvent(
        entries1, 0, NetLog::TYPE_PROXY_SERVICE));
    EXPECT_TRUE(LogContainsBeginEvent(
        entries1, 1, NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC));
    // Note that TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC is never completed before
    // the cancellation occured.
    EXPECT_TRUE(LogContainsEvent(
        entries1, 2, NetLog::TYPE_CANCELLED, NetLog::PHASE_NONE));
    EXPECT_TRUE(LogContainsEndEvent(
        entries1, 3, NetLog::TYPE_PROXY_SERVICE));
}

// Test that if auto-detect fails, we fall-back to the custom pac.
TEST_F(ProxyServiceTest, FallbackFromAutodetectToCustomPac)
{
    const GURL url1("http://request1");
    const GURL url2("http://request2");
    ProxyConfig config;
    config.set_auto_detect(true);
    config.set_pac_url(GURL("http://foopy/proxy.pac"));
    config.proxy_rules().ParseFromString("http=foopy:80"); // Won't be used.

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 2 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info1,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info2,
        callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // It should be trying to auto-detect first -- FAIL the autodetect during
    // the script download.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    // Next it should be trying the custom PAC url.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    // Now finally, the pending requests should have been sent to the resolver
    // (which was initialized with custom PAC script).

    RequestMap requests = GetPendingRequestsForURLs(resolver, url1, url2);

    // Complete the pending requests.
    requests[url2]->results()->UseNamedProxy("request2:80");
    requests[url2]->CompleteNow(OK);
    requests[url1]->results()->UseNamedProxy("request1:80");
    requests[url1]->CompleteNow(OK);

    // Verify that requests ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());
    EXPECT_FALSE(info1.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info1.proxy_resolve_end_time().is_null());
    EXPECT_LE(info1.proxy_resolve_start_time(), info1.proxy_resolve_end_time());

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
    EXPECT_FALSE(info2.proxy_resolve_start_time().is_null());
    EXPECT_FALSE(info2.proxy_resolve_end_time().is_null());
    EXPECT_LE(info2.proxy_resolve_start_time(), info2.proxy_resolve_end_time());
}

// This is the same test as FallbackFromAutodetectToCustomPac, except
// the auto-detect script fails parsing rather than downloading.
TEST_F(ProxyServiceTest, FallbackFromAutodetectToCustomPac2)
{
    const GURL url1("http://request1");
    const GURL url2("http://request2");
    ProxyConfig config;
    config.set_auto_detect(true);
    config.set_pac_url(GURL("http://foopy/proxy.pac"));
    config.proxy_rules().ParseFromString("http=foopy:80"); // Won't be used.

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 2 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(url1, std::string(), LOAD_NORMAL, &info1,
        callback1.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(url2, std::string(), LOAD_NORMAL, &info2,
        callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // It should be trying to auto-detect first -- succeed the download.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, "invalid-script-contents");

    // The script contents passed failed basic verification step (since didn't
    // contain token FindProxyForURL), so it was never passed to the resolver.

    // Next it should be trying the custom PAC url.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    // Now finally, the pending requests should have been sent to the resolver
    // (which was initialized with custom PAC script).

    RequestMap requests = GetPendingRequestsForURLs(resolver, url1, url2);

    // Complete the pending requests.
    requests[url2]->results()->UseNamedProxy("request2:80");
    requests[url2]->CompleteNow(OK);
    requests[url1]->results()->UseNamedProxy("request1:80");
    requests[url1]->CompleteNow(OK);

    // Verify that requests ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}

// Test that if all of auto-detect, a custom PAC script, and manual settings
// are given, then we will try them in that order.
TEST_F(ProxyServiceTest, FallbackFromAutodetectToCustomToManual)
{
    ProxyConfig config;
    config.set_auto_detect(true);
    config.set_pac_url(GURL("http://foopy/proxy.pac"));
    config.proxy_rules().ParseFromString("http=foopy:80");

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 2 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ProxyInfo info2;
    TestCompletionCallback callback2;
    ProxyService::PacRequest* request2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), &request2, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // It should be trying to auto-detect first -- fail the download.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    // Next it should be trying the custom PAC url -- fail the download.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    // Since we never managed to initialize a resolver, nothing should have been
    // sent to it.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // Verify that requests ran as expected -- they should have fallen back to
    // the manual proxy configuration for HTTP urls.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("foopy:80", info1.proxy_server().ToURI());

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("foopy:80", info2.proxy_server().ToURI());
}

// Test that the bypass rules are NOT applied when using autodetect.
TEST_F(ProxyServiceTest, BypassDoesntApplyToPac)
{
    ProxyConfig config;
    config.set_auto_detect(true);
    config.set_pac_url(GURL("http://foopy/proxy.pac"));
    config.proxy_rules().ParseFromString("http=foopy:80"); // Not used.
    config.proxy_rules().bypass_rules.ParseFromString("www.google.com");

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 requests.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://www.google.com"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // It should be trying to auto-detect first -- succeed the download.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://wpad/wpad.dat"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://www.google.com"),
        resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Verify that request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // Start another request, it should pickup the bypass item.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://www.google.com"), std::string(),
        LOAD_NORMAL, &info2, callback2.callback(), nullptr,
        nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://www.google.com"),
        resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}

// Delete the ProxyService while InitProxyResolver has an outstanding
// request to the script fetcher. When run under valgrind, should not
// have any memory errors (used to be that the ProxyScriptFetcher was
// being deleted prior to the InitProxyResolver).
TEST_F(ProxyServiceTest, DeleteWhileInitProxyResolverHasOutstandingFetch)
{
    ProxyConfig config = ProxyConfig::CreateFromCustomPacURL(GURL("http://foopy/proxy.pac"));

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://www.google.com"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that nothing has been sent to the proxy resolver factory yet.
    ASSERT_EQ(0u, factory->pending_requests().size());

    // InitProxyResolver should have issued a request to the ProxyScriptFetcher
    // and be waiting on that to complete.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
}

// Delete the ProxyService while InitProxyResolver has an outstanding
// request to the proxy resolver. When run under valgrind, should not
// have any memory errors (used to be that the ProxyResolver was
// being deleted prior to the InitProxyResolver).
TEST_F(ProxyServiceTest, DeleteWhileInitProxyResolverHasOutstandingSet)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    TestCompletionCallback callback;
    int rv = service.ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
        callback.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    EXPECT_EQ(GURL("http://foopy/proxy.pac"),
        factory->pending_requests()[0]->script_data()->url());
}

TEST_F(ProxyServiceTest, ResetProxyConfigService)
{
    ProxyConfig config1;
    config1.proxy_rules().ParseFromString("foopy1:8080");
    config1.set_auto_detect(false);
    ProxyService service(base::WrapUnique(new MockProxyConfigService(config1)),
        nullptr, nullptr);

    ProxyInfo info;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy1:8080", info.proxy_server().ToURI());

    ProxyConfig config2;
    config2.proxy_rules().ParseFromString("foopy2:8080");
    config2.set_auto_detect(false);
    service.ResetConfigService(
        base::WrapUnique(new MockProxyConfigService(config2)));
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_EQ("foopy2:8080", info.proxy_server().ToURI());
}

// Test that when going from a configuration that required PAC to one
// that does NOT, we unset the variable |should_use_proxy_resolver_|.
TEST_F(ProxyServiceTest, UpdateConfigFromPACToDirect)
{
    ProxyConfig config = ProxyConfig::CreateAutoDetect();

    MockProxyConfigService* config_service = new MockProxyConfigService(config);
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);
    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://www.google.com"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Successfully set the autodetect script.
    EXPECT_EQ(ProxyResolverScriptData::TYPE_AUTO_DETECT,
        factory->pending_requests()[0]->script_data()->type());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    // Complete the pending request.
    ASSERT_EQ(1u, resolver.pending_requests().size());
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Verify that request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // Force the ProxyService to pull down a new proxy configuration.
    // (Even though the configuration isn't old/bad).
    //
    // This new configuration no longer has auto_detect set, so
    // requests should complete synchronously now as direct-connect.
    config_service->SetConfig(ProxyConfig::CreateDirect());

    // Start another request -- the effective configuration has changed.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://www.google.com"), std::string(),
        LOAD_NORMAL, &info2, callback2.callback(), nullptr,
        nullptr, BoundNetLog());
    EXPECT_EQ(OK, rv);

    EXPECT_TRUE(info2.is_direct());
}

TEST_F(ProxyServiceTest, NetworkChangeTriggersPacRefetch)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    TestNetLog log;

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), &log);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Disable the "wait after IP address changes" hack, so this unit-test can
    // complete quickly.
    service.set_stall_proxy_auto_config_delay(base::TimeDelta());

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, the request will have been sent to
    // the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request1"), resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // Now simluate a change in the network. The ProxyConfigService is still
    // going to return the same PAC URL as before, but this URL needs to be
    // refetched on the new network.
    NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
    base::RunLoop().RunUntilIdle(); // Notification happens async.

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // This second request should have triggered the re-download of the PAC
    // script (since we marked the network as having changed).
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // Simulate the PAC script fetch as having completed (this time with
    // different data).
    fetcher->NotifyFetchCompletion(OK, kValidPacScript2);

    // Now that the PAC script is downloaded, the second request will have been
    // sent to the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript2),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request2"), resolver.pending_requests()[0]->url());

    // Complete the pending second request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());

    // Check that the expected events were output to the log stream. In particular
    // PROXY_CONFIG_CHANGED should have only been emitted once (for the initial
    // setup), and NOT a second time when the IP address changed.
    TestNetLogEntry::List entries;
    log.GetEntries(&entries);

    EXPECT_TRUE(LogContainsEntryWithType(entries, 0,
        NetLog::TYPE_PROXY_CONFIG_CHANGED));
    ASSERT_EQ(9u, entries.size());
    for (size_t i = 1; i < entries.size(); ++i)
        EXPECT_NE(NetLog::TYPE_PROXY_CONFIG_CHANGED, entries[i].type);
}

// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch fails due
// to a network error, we will eventually re-configure the service to use the
// script once it becomes available.
TEST_F(ProxyServiceTest, PACScriptRefetchAfterFailure)
{
    // Change the retry policy to wait a mere 1 ms before retrying, so the test
    // runs quickly.
    ImmediatePollPolicy poll_policy;
    ProxyService::set_pac_script_poll_policy(&poll_policy);

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    //
    // We simulate a failed download attempt, the proxy service should now
    // fall-back to DIRECT connections.
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    ASSERT_TRUE(factory->pending_requests().empty());

    // Wait for completion callback, and verify it used DIRECT.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_TRUE(info1.is_direct());

    // At this point we have initialized the proxy service using a PAC script,
    // however it failed and fell-back to DIRECT.
    //
    // A background task to periodically re-check the PAC script for validity will
    // have been started. We will now wait for the next download attempt to start.
    //
    // Note that we shouldn't have to wait long here, since our test enables a
    // special unit-test mode.
    fetcher->WaitUntilFetch();

    ASSERT_TRUE(factory->pending_requests().empty());

    // Make sure that our background checker is trying to download the expected
    // PAC script (same one as before). This time we will simulate a successful
    // download of the script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    base::RunLoop().RunUntilIdle();

    // Now that the PAC script is downloaded, it should be used to initialize the
    // ProxyResolver. Simulate a successful parse.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    // At this point the ProxyService should have re-configured itself to use the
    // PAC script (thereby recovering from the initial fetch failure). We will
    // verify that the next Resolve request uses the resolver rather than
    // DIRECT.

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that it was sent to the resolver.
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request2"), resolver.pending_requests()[0]->url());

    // Complete the pending second request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}

// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds,
// however at a later time its *contents* change, we will eventually
// re-configure the service to use the new script.
TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentChange)
{
    // Change the retry policy to wait a mere 1 ms before retrying, so the test
    // runs quickly.
    ImmediatePollPolicy poll_policy;
    ProxyService::set_pac_script_poll_policy(&poll_policy);

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, the request will have been sent to
    // the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request1"), resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // At this point we have initialized the proxy service using a PAC script.
    //
    // A background task to periodically re-check the PAC script for validity will
    // have been started. We will now wait for the next download attempt to start.
    //
    // Note that we shouldn't have to wait long here, since our test enables a
    // special unit-test mode.
    fetcher->WaitUntilFetch();

    ASSERT_TRUE(factory->pending_requests().empty());
    ASSERT_TRUE(resolver.pending_requests().empty());

    // Make sure that our background checker is trying to download the expected
    // PAC script (same one as before). This time we will simulate a successful
    // download of a DIFFERENT script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript2);

    base::RunLoop().RunUntilIdle();

    // Now that the PAC script is downloaded, it should be used to initialize the
    // ProxyResolver. Simulate a successful parse.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript2),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    // At this point the ProxyService should have re-configured itself to use the
    // new PAC script.

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that it was sent to the resolver.
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request2"), resolver.pending_requests()[0]->url());

    // Complete the pending second request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}

// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds
// and so does the next poll, however the contents of the downloaded script
// have NOT changed, then we do not bother to re-initialize the proxy resolver.
TEST_F(ProxyServiceTest, PACScriptRefetchAfterContentUnchanged)
{
    // Change the retry policy to wait a mere 1 ms before retrying, so the test
    // runs quickly.
    ImmediatePollPolicy poll_policy;
    ProxyService::set_pac_script_poll_policy(&poll_policy);

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, the request will have been sent to
    // the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request1"), resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // At this point we have initialized the proxy service using a PAC script.
    //
    // A background task to periodically re-check the PAC script for validity will
    // have been started. We will now wait for the next download attempt to start.
    //
    // Note that we shouldn't have to wait long here, since our test enables a
    // special unit-test mode.
    fetcher->WaitUntilFetch();

    ASSERT_TRUE(factory->pending_requests().empty());
    ASSERT_TRUE(resolver.pending_requests().empty());

    // Make sure that our background checker is trying to download the expected
    // PAC script (same one as before). We will simulate the same response as
    // last time (i.e. the script is unchanged).
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    base::RunLoop().RunUntilIdle();

    ASSERT_TRUE(factory->pending_requests().empty());
    ASSERT_TRUE(resolver.pending_requests().empty());

    // At this point the ProxyService is still running the same PAC script as
    // before.

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // Check that it was sent to the resolver.
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request2"), resolver.pending_requests()[0]->url());

    // Complete the pending second request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());
}

// This test verifies that the PAC script specified by the settings is
// periodically polled for changes. Specifically, if the initial fetch succeeds,
// however at a later time it starts to fail, we should re-configure the
// ProxyService to stop using that PAC script.
TEST_F(ProxyServiceTest, PACScriptRefetchAfterSuccess)
{
    // Change the retry policy to wait a mere 1 ms before retrying, so the test
    // runs quickly.
    ImmediatePollPolicy poll_policy;
    ProxyService::set_pac_script_poll_policy(&poll_policy);

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, the request will have been sent to
    // the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request1"), resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // At this point we have initialized the proxy service using a PAC script.
    //
    // A background task to periodically re-check the PAC script for validity will
    // have been started. We will now wait for the next download attempt to start.
    //
    // Note that we shouldn't have to wait long here, since our test enables a
    // special unit-test mode.
    fetcher->WaitUntilFetch();

    ASSERT_TRUE(factory->pending_requests().empty());
    ASSERT_TRUE(resolver.pending_requests().empty());

    // Make sure that our background checker is trying to download the expected
    // PAC script (same one as before). This time we will simulate a failure
    // to download the script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    base::RunLoop().RunUntilIdle();

    // At this point the ProxyService should have re-configured itself to use
    // DIRECT connections rather than the given proxy resolver.

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_TRUE(info2.is_direct());
}

// Tests that the code which decides at what times to poll the PAC
// script follows the expected policy.
TEST_F(ProxyServiceTest, PACScriptPollingPolicy)
{
    // Retrieve the internal polling policy implementation used by ProxyService.
    std::unique_ptr<ProxyService::PacPollPolicy> policy = ProxyService::CreateDefaultPacPollPolicy();

    int error;
    ProxyService::PacPollPolicy::Mode mode;
    const base::TimeDelta initial_delay = base::TimeDelta::FromMilliseconds(-1);
    base::TimeDelta delay = initial_delay;

    // --------------------------------------------------
    // Test the poll sequence in response to a failure.
    // --------------------------------------------------
    error = ERR_NAME_NOT_RESOLVED;

    // Poll #0
    mode = policy->GetNextDelay(error, initial_delay, &delay);
    EXPECT_EQ(8, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_USE_TIMER, mode);

    // Poll #1
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(32, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // Poll #2
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(120, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // Poll #3
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(14400, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // Poll #4
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(14400, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // --------------------------------------------------
    // Test the poll sequence in response to a success.
    // --------------------------------------------------
    error = OK;

    // Poll #0
    mode = policy->GetNextDelay(error, initial_delay, &delay);
    EXPECT_EQ(43200, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // Poll #1
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(43200, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);

    // Poll #2
    mode = policy->GetNextDelay(error, delay, &delay);
    EXPECT_EQ(43200, delay.InSeconds());
    EXPECT_EQ(ProxyService::PacPollPolicy::MODE_START_AFTER_ACTIVITY, mode);
}

// This tests the polling of the PAC script. Specifically, it tests that
// polling occurs in response to user activity.
TEST_F(ProxyServiceTest, PACScriptRefetchAfterActivity)
{
    ImmediateAfterActivityPollPolicy poll_policy;
    ProxyService::set_pac_script_poll_policy(&poll_policy);

    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(true);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    MockProxyScriptFetcher* fetcher = new MockProxyScriptFetcher;
    service.SetProxyScriptFetchers(
        fetcher, base::WrapUnique(new DoNothingDhcpProxyScriptFetcher()));

    // Start 1 request.

    ProxyInfo info1;
    TestCompletionCallback callback1;
    int rv = service.ResolveProxy(GURL("http://request1"), std::string(),
        LOAD_NORMAL, &info1, callback1.callback(),
        nullptr, nullptr, BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // The first request should have triggered initial download of PAC script.
    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // Nothing has been sent to the factory yet.
    EXPECT_TRUE(factory->pending_requests().empty());

    // At this point the ProxyService should be waiting for the
    // ProxyScriptFetcher to invoke its completion callback, notifying it of
    // PAC script download completion.
    fetcher->NotifyFetchCompletion(OK, kValidPacScript1);

    // Now that the PAC script is downloaded, the request will have been sent to
    // the proxy resolver.
    EXPECT_EQ(ASCIIToUTF16(kValidPacScript1),
        factory->pending_requests()[0]->script_data()->utf16());
    factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request1"), resolver.pending_requests()[0]->url());

    // Complete the pending request.
    resolver.pending_requests()[0]->results()->UseNamedProxy("request1:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    // Wait for completion callback, and verify that the request ran as expected.
    EXPECT_EQ(OK, callback1.WaitForResult());
    EXPECT_EQ("request1:80", info1.proxy_server().ToURI());

    // At this point we have initialized the proxy service using a PAC script.
    // Our PAC poller is set to update ONLY in response to network activity,
    // (i.e. another call to ResolveProxy()).

    ASSERT_FALSE(fetcher->has_pending_request());
    ASSERT_TRUE(factory->pending_requests().empty());
    ASSERT_TRUE(resolver.pending_requests().empty());

    // Start a second request.
    ProxyInfo info2;
    TestCompletionCallback callback2;
    rv = service.ResolveProxy(GURL("http://request2"), std::string(), LOAD_NORMAL,
        &info2, callback2.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(ERR_IO_PENDING, rv);

    // This request should have sent work to the resolver; complete it.
    ASSERT_EQ(1u, resolver.pending_requests().size());
    EXPECT_EQ(GURL("http://request2"), resolver.pending_requests()[0]->url());
    resolver.pending_requests()[0]->results()->UseNamedProxy("request2:80");
    resolver.pending_requests()[0]->CompleteNow(OK);

    EXPECT_EQ(OK, callback2.WaitForResult());
    EXPECT_EQ("request2:80", info2.proxy_server().ToURI());

    // In response to getting that resolve request, the poller should have
    // started the next poll, and made it as far as to request the download.

    EXPECT_TRUE(fetcher->has_pending_request());
    EXPECT_EQ(GURL("http://foopy/proxy.pac"), fetcher->pending_request_url());

    // This time we will fail the download, to simulate a PAC script change.
    fetcher->NotifyFetchCompletion(ERR_FAILED, std::string());

    // Drain the message loop, so ProxyService is notified of the change
    // and has a chance to re-configure itself.
    base::RunLoop().RunUntilIdle();

    // Start a third request -- this time we expect to get a direct connection
    // since the PAC script poller experienced a failure.
    ProxyInfo info3;
    TestCompletionCallback callback3;
    rv = service.ResolveProxy(GURL("http://request3"), std::string(), LOAD_NORMAL,
        &info3, callback3.callback(), nullptr, nullptr,
        BoundNetLog());
    EXPECT_EQ(OK, rv);
    EXPECT_TRUE(info3.is_direct());
}

// Test that the synchronous resolution fails when a PAC script is active.
TEST_F(ProxyServiceTest, SynchronousWithPAC)
{
    MockProxyConfigService* config_service = new MockProxyConfigService("http://foopy/proxy.pac");

    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(config_service),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    info.UseDirect();
    BoundTestNetLog log;

    bool synchronous_success = service.TryResolveProxySynchronously(
        url, std::string(), LOAD_NORMAL, &info, nullptr, log.bound());
    EXPECT_FALSE(synchronous_success);

    // |info| should not have been modified.
    EXPECT_TRUE(info.is_direct());
}

// Test that synchronous results are returned correctly if a fixed proxy
// configuration is active.
TEST_F(ProxyServiceTest, SynchronousWithFixedConfiguration)
{
    ProxyConfig config;
    config.proxy_rules().ParseFromString("foopy1:8080");
    config.set_auto_detect(false);

    MockAsyncProxyResolverFactory* factory = new MockAsyncProxyResolverFactory(false);

    ProxyService service(base::WrapUnique(new MockProxyConfigService(config)),
        base::WrapUnique(factory), nullptr);

    GURL url("http://www.google.com/");

    ProxyInfo info;
    BoundTestNetLog log;

    bool synchronous_success = service.TryResolveProxySynchronously(
        url, std::string(), LOAD_NORMAL, &info, nullptr, log.bound());
    EXPECT_TRUE(synchronous_success);
    EXPECT_FALSE(info.is_direct());
    EXPECT_EQ("foopy1", info.proxy_server().host_port_pair().host());

    // No request should have been queued.
    EXPECT_EQ(0u, factory->pending_requests().size());
}

// Helper class to exercise URL sanitization using the different policies. This
// works by submitted URLs to the ProxyService. In turn the ProxyService
// sanitizes the URL and then passes it along to the ProxyResolver. This helper
// returns the URL seen by the ProxyResolver.
class SanitizeUrlHelper {
public:
    SanitizeUrlHelper()
    {
        std::unique_ptr<MockProxyConfigService> config_service(
            new MockProxyConfigService("http://foopy/proxy.pac"));

        factory = new MockAsyncProxyResolverFactory(false);

        service_.reset(new ProxyService(std::move(config_service),
            base::WrapUnique(factory), nullptr));

        // Do an initial request to initialize the service (configure the PAC
        // script).
        GURL url("http://example.com");

        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service_->ResolveProxy(url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(ERR_IO_PENDING, rv);

        // First step is to download the PAC script.
        EXPECT_EQ(GURL("http://foopy/proxy.pac"),
            factory->pending_requests()[0]->script_data()->url());
        factory->pending_requests()[0]->CompleteNowWithForwarder(OK, &resolver);

        EXPECT_EQ(1u, resolver.pending_requests().size());
        EXPECT_EQ(url, resolver.pending_requests()[0]->url());

        // Complete the request.
        resolver.pending_requests()[0]->results()->UsePacString("DIRECT");
        resolver.pending_requests()[0]->CompleteNow(OK);
        EXPECT_EQ(OK, callback.WaitForResult());
        EXPECT_TRUE(info.is_direct());
    }

    // Changes the URL sanitization policy for the underlying ProxyService. This
    // will affect subsequent calls to SanitizeUrl.
    void SetSanitizeUrlPolicy(ProxyService::SanitizeUrlPolicy policy)
    {
        service_->set_sanitize_url_policy(policy);
    }

    // Makes a proxy resolution request through the ProxyService, and returns the
    // URL that was submitted to the Proxy Resolver.
    GURL SanitizeUrl(const GURL& raw_url)
    {
        // Issue a request and see what URL is sent to the proxy resolver.
        ProxyInfo info;
        TestCompletionCallback callback;
        int rv = service_->ResolveProxy(raw_url, std::string(), LOAD_NORMAL, &info,
            callback.callback(), nullptr, nullptr,
            BoundNetLog());
        EXPECT_EQ(ERR_IO_PENDING, rv);

        EXPECT_EQ(1u, resolver.pending_requests().size());

        GURL sanitized_url = resolver.pending_requests()[0]->url();

        // Complete the request.
        resolver.pending_requests()[0]->results()->UsePacString("DIRECT");
        resolver.pending_requests()[0]->CompleteNow(OK);
        EXPECT_EQ(OK, callback.WaitForResult());
        EXPECT_TRUE(info.is_direct());

        return sanitized_url;
    }

    // Changes the ProxyService's URL sanitization policy and then sanitizes
    // |raw_url|.
    GURL SanitizeUrl(const GURL& raw_url,
        ProxyService::SanitizeUrlPolicy policy)
    {
        service_->set_sanitize_url_policy(policy);
        return SanitizeUrl(raw_url);
    }

private:
    MockAsyncProxyResolver resolver;
    MockAsyncProxyResolverFactory* factory;
    std::unique_ptr<ProxyService> service_;
};

TEST_F(ProxyServiceTest, SanitizeUrlDefaultsToSafe)
{
    SanitizeUrlHelper helper;

    // Without changing the URL sanitization policy, the default should be to
    // strip https:// URLs.
    EXPECT_EQ(GURL("https://example.com/"),
        helper.SanitizeUrl(
            GURL("https://foo:bar@example.com/foo/bar/baz?hello#sigh")));
}

// Tests URL sanitization with input URLs that have a // non-cryptographic
// scheme (i.e. http://). The sanitized result is consistent regardless of the
// stripping mode selected.
TEST_F(ProxyServiceTest, SanitizeUrlForPacScriptNonCryptographic)
{
    const struct {
        const char* raw_url;
        const char* sanitized_url;
    } kTests[] = {
        // Embedded identity is stripped.
        {
            "http://foo:bar@example.com/",
            "http://example.com/",
        },
        {
            "ftp://foo:bar@example.com/",
            "ftp://example.com/",
        },
        {
            "ftp://example.com/some/path/here",
            "ftp://example.com/some/path/here",
        },
        // Reference fragment is stripped.
        {
            "http://example.com/blah#hello",
            "http://example.com/blah",
        },
        // Query parameters are NOT stripped.
        {
            "http://example.com/foo/bar/baz?hello",
            "http://example.com/foo/bar/baz?hello",
        },
        // Fragment is stripped, but path and query are left intact.
        {
            "http://foo:bar@example.com/foo/bar/baz?hello#sigh",
            "http://example.com/foo/bar/baz?hello",
        },
        // Port numbers are not affected.
        {
            "http://example.com:88/hi",
            "http://example.com:88/hi",
        },
    };

    SanitizeUrlHelper helper;

    for (const auto& test : kTests) {
        // The result of SanitizeUrlForPacScript() is the same regardless of the
        // second parameter (sanitization mode), since the input URLs do not use a
        // cryptographic scheme.
        GURL raw_url(test.raw_url);
        ASSERT_TRUE(raw_url.is_valid());
        EXPECT_FALSE(raw_url.SchemeIsCryptographic());

        EXPECT_EQ(
            GURL(test.sanitized_url),
            helper.SanitizeUrl(raw_url, ProxyService::SanitizeUrlPolicy::UNSAFE));

        EXPECT_EQ(
            GURL(test.sanitized_url),
            helper.SanitizeUrl(raw_url, ProxyService::SanitizeUrlPolicy::SAFE));
    }
}

// Tests URL sanitization using input URLs that have a cryptographic schemes
// (i.e. https://). The sanitized result differs depending on the sanitization
// mode chosen.
TEST_F(ProxyServiceTest, SanitizeUrlForPacScriptCryptographic)
{
    const struct {
        // Input URL.
        const char* raw_url;

        // Output URL when stripping of cryptographic URLs is disabled.
        const char* sanitized_url_unstripped;

        // Output URL when stripping of cryptographic URLs is enabled.
        const char* sanitized_url;
    } kTests[] = {
        // Embedded identity is always stripped.
        {
            "https://foo:bar@example.com/",
            "https://example.com/",
            "https://example.com/",
        },
        // Fragments are always stripped, but stripping path is conditional on the
        // mode.
        {
            "https://example.com/blah#hello",
            "https://example.com/blah",
            "https://example.com/",
        },
        // Stripping the query is conditional on the mode.
        {
            "https://example.com/?hello",
            "https://example.com/?hello",
            "https://example.com/",
        },
        // The embedded identity and fragment is always stripped, however path and
        // query are conditional on the stripping mode.
        {
            "https://foo:bar@example.com/foo/bar/baz?hello#sigh",
            "https://example.com/foo/bar/baz?hello",
            "https://example.com/",
        },
        // The URL's port should not be stripped.
        {
            "https://example.com:88/hi",
            "https://example.com:88/hi",
            "https://example.com:88/",
        },
        // Try a wss:// URL, to make sure it also strips (is is also a
        // cryptographic URL).
        {
            "wss://example.com:88/hi",
            "wss://example.com:88/hi",
            "wss://example.com:88/",
        },
    };

    SanitizeUrlHelper helper;

    for (const auto& test : kTests) {
        GURL raw_url(test.raw_url);
        ASSERT_TRUE(raw_url.is_valid());
        EXPECT_TRUE(raw_url.SchemeIsCryptographic());

        EXPECT_EQ(
            GURL(test.sanitized_url_unstripped),
            helper.SanitizeUrl(raw_url, ProxyService::SanitizeUrlPolicy::UNSAFE));

        EXPECT_EQ(
            GURL(test.sanitized_url),
            helper.SanitizeUrl(raw_url, ProxyService::SanitizeUrlPolicy::SAFE));
    }
}

} // namespace net
